﻿namespace Socket.Core
{
	using System;
	using System.Collections;
	using System.Collections.Generic;
	using System.Diagnostics;
	using System.Runtime.CompilerServices;
	using System.Threading;

	public class LockFreeStack<T> : IEnumerable<T>
	{
		private long _count;
		private Node<T> _nodeList;

		public T Pop()
		{
			T local;
			try
			{
				Node<T> node = this._nodeList;
				if (node == null)
				{
					throw new ArgumentNullException("stack empty!");
				}
				this._nodeList = node.Next;
				local = node.Value;
			}
			finally
			{
				Interlocked.Decrement(ref this._count);
			}
			return local;
		}

		public void Push(T value)
		{
			try
			{
				Node<T> node = new Node<T>
				{
					Value = value
				};
				node.Next = this._nodeList;
				this._nodeList = node;
			}
			finally
			{
				Interlocked.Increment(ref this._count);
			}
		}

		public IEnumerator<T> GetEnumerator()
		{
			while (_nodeList != null)
			{
				yield return _nodeList.Value;
				_nodeList = _nodeList.Next;
			}
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			Interlocked.Exchange(ref this._count, 0L);
			return this.GetEnumerator();
		}

		public long Count
		{
			get { return Interlocked.Read(ref _count); }
			set { _count = value; }
		}

		private class Node<T>
		{
			internal T Value;
			internal Node<T> Next;
		}
	}
}